/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.loaders;
import java.io.*;
import java.util.*;
import org.openide.cookies.ConnectionCookie;
import org.openide.nodes.Node;
/** Support implementing ConnectionCookie, that stores
* listeners in extended attributes of associated entry.
*
* @author Jaroslav Tulach, Petr Hamernik
*/
public class ConnectionSupport extends Object implements ConnectionCookie {
/** extended attribute to store (ArrayList of Type and Node.Handle) */
private static final String EA_LISTENERS = "EA-OpenIDE-Connection"; // NOI18N
/** entry to work on */
private MultiDataObject.Entry entry;
/** array of types */
private ConnectionCookie.Type[] types;
/** cached the return value for getTypes method */
private Set typesSet;
/** table of listeners for non-persistent types. Array of Pairs.
*/
private LinkedList listeners;
/** Creates new connection support for given file entry.
* @param entry entry to store listener to its extended attributes
* @param types a list of event types to support
*/
public ConnectionSupport (MultiDataObject.Entry entry, ConnectionCookie.Type[] types) {
this.entry = entry;
this.types = types;
}
/** Attaches new node to listen to events produced by this
* event. The type must be one of event types supported by this
* cookie and the listener should have ConnectionCookie.Listener cookie
* attached so it can be notified when event of requested type occurs.
*
* @param type the type of event, must be supported by the cookie
* @param listener the node that should be notified
*
* @exception InvalidObjectException if the type is not supported by the cookie (subclass of IOException)
* @exception IOException if the type is persistent and the listener does not
* have serializable handle (listener.getHandle () is null or its serialization
* throws an exception)
*/
public synchronized void register (ConnectionCookie.Type type, Node listener) throws IOException {
// test if the file is supported, if not throws exception
testSupported (type);
boolean persistent = type.isPersistent ();
LinkedList list;
if (persistent) {
list = (LinkedList)entry.getFile ().getAttribute (EA_LISTENERS);
} else {
list = listeners;
}
if (list == null) {
// empty list => create new
list = new LinkedList ();
}
// System.out.println("======================================== ADD:"+entry.getFile().getName()); // NOI18N
// System.out.println(this);
// System.out.println("size:"+list.size()); // NOI18N
Iterator it = list.iterator ();
while (it.hasNext ()) {
Pair pair = (Pair)it.next ();
// System.out.println("test:"+pair.getType()); // NOI18N
if (type.equals (pair.getType ())) {
Node n;
try {
n = pair.getNode ();
// System.out.println(" node:"+n); // NOI18N
} catch (IOException e) {
// node that cannot produce handle => remove it
if (System.getProperty ("netbeans.debug.exceptions") != null) e.printStackTrace();
it.remove ();
// go on
continue;
}
// System.out.println(" compare with:"+listener); // NOI18N
if (n.equals (listener)) {
// we found our node - it is already in the list.
// System.out.println("the listener found - remove it."); // NOI18N
it.remove();
continue;
}
else {
// System.out.println(" nene"); // NOI18N
}
}
}
list.add (persistent ? new Pair (type, listener.getHandle ()) : new Pair (type, listener));
// System.out.println("after add:"+list.size()); // NOI18N
if (persistent) {
// save can throw IOException
entry.getFile ().setAttribute (EA_LISTENERS, list);
}
}
/** Unregisters an listener.
* @param type type of event to unregister the listener from listening to
* @param listener to unregister
* @exception IOException if there is I/O operation error when the removing
* the listener from persistent storage
*/
public synchronized void unregister (ConnectionCookie.Type type, Node listener) throws IOException {
// test if the file is supported, if not throws exception
testSupported (type);
boolean persistent = type.isPersistent ();
LinkedList list;
if (persistent) {
list = (LinkedList)entry.getFile ().getAttribute (EA_LISTENERS);
} else {
list = listeners;
}
if (list == null) {
// empty list => no work
return;
}
// System.out.println("======================================== REMOVE:"+entry.getFile().getName()); // NOI18N
// System.out.println(this);
// System.out.println("size:"+list.size()); // NOI18N
Iterator it = list.iterator ();
while (it.hasNext ()) {
Pair pair = (Pair)it.next ();
if (type.equals (pair.getType ())) {
Node n;
try {
n = pair.getNode ();
} catch (IOException e) {
// node that cannot produce handle => remove it
it.remove ();
// go on
continue;
}
if (n.equals (listener)) {
// we found our node
it.remove ();
// break the cycle but save if necessary
continue;
}
}
}
//System.out.println("after remove:"+list.size()); // NOI18N
if (persistent) {
// save can throw IOException
entry.getFile ().setAttribute (EA_LISTENERS, list);
}
}
/** Unmutable set of types supported by this connection source.
* @return a set of Type objects
*/
public java.util.Set getTypes () {
if (typesSet == null)
typesSet = Collections.unmodifiableSet (new HashSet (Arrays.asList (types)));
return typesSet;
}
/** Get the list of all registered types in every (persistent
* or not persistent) connections.
*
* @return the list of ConnectionCookie.Type objects
*/
public List getRegisteredTypes() {
LinkedList typesList = new LinkedList();
LinkedList list = listeners;
for (int i = 0; i <= 1; i++) {
if (i == 1)
list = (LinkedList)entry.getFile ().getAttribute (EA_LISTENERS);
if (list == null)
continue;
Iterator it = list.iterator ();
while (it.hasNext ()) {
typesList.add(((Pair)it.next()).getType());
}
}
return typesList;
}
/** Fires info for all listeners of given type.
* @param ev the event
*/
public synchronized void fireEvent (ConnectionCookie.Event ev) {
LinkedList list;
ConnectionCookie.Type type = ev.getType ();
boolean persistent = type.isPersistent ();
if (persistent) {
list = (LinkedList)entry.getFile ().getAttribute (EA_LISTENERS);
} else {
list = listeners;
}
if (list == null) return;
int size = list.size ();
Iterator it = list.iterator ();
while (it.hasNext ()) {
Pair pair = (Pair)it.next ();
if (pair.getType ().overlaps(ev.getType())) {
try {
ConnectionCookie.Listener l = (ConnectionCookie.Listener)pair.getNode ().getCookie (ConnectionCookie.Listener.class);
if (l != null) {
try {
l.notify (ev);
} catch (IllegalArgumentException e) {
it.remove ();
} catch (ClassCastException e) {
it.remove ();
}
}
} catch (IOException e) {
it.remove ();
}
}
}
if (persistent) {
// save can throw IOException
try {
entry.getFile ().setAttribute (EA_LISTENERS, list);
} catch (IOException e) {
// ignore never mind
}
}
}
/** Obtains a set of all listeners for given type.
* @param type type of events to test
* @return unmutable set of all listeners (Node) for a type
*/
public synchronized java.util.Set listenersFor (ConnectionCookie.Type type) {
LinkedList list;
if (type.isPersistent ()) {
list = (LinkedList)entry.getFile ().getAttribute (EA_LISTENERS);
} else {
list = listeners;
}
if (list == null) return Collections.EMPTY_SET;
Iterator it = list.iterator ();
HashSet set = new HashSet (7);
while (it.hasNext ()) {
Pair pair = (Pair)it.next ();
if (type.overlaps(pair.getType ())) {
try {
set.add (pair.getNode ());
} catch (IOException e) {
// ignore the exception
}
}
}
return set;
}
/** Test if the type is supported.
* @param t type
* @exception InvalidObjectException if type is not valid
*/
private void testSupported (ConnectionCookie.Type t) throws InvalidObjectException {
for (int i = 0; i < types.length; i++) {
if (t.overlaps(types[i])) {
return;
}
}
throw new InvalidObjectException (t.toString ());
}
/** A pair of type of event and a handle to it.
*/
private static final class Pair extends Object implements java.io.Serializable {
/** type of the listener */
private ConnectionCookie.Type type;
/** the node or the handle to a node */
private Object value;
static final long serialVersionUID =387180886175136728L;
/** @param t the type of the event
* @param n the listener
*/
public Pair (ConnectionCookie.Type t, Node n) {
type = t;
value = n;
}
/** @param t the type of the event
* @param h the listener's handle
* @exception IOException if handle is null
*/
public Pair (ConnectionCookie.Type t, Node.Handle h) throws IOException {
if (h == null) throw new IOException ();
type = t;
value = h;
}
/** Getter of the type.
*/
public ConnectionCookie.Type getType () {
return type;
}
/** Getter of the listener.
* @return listener's node
* @exception IOException if the handle is not able to create a node
*/
public Node getNode () throws IOException {
return value instanceof Node ? (Node)value : ((Node.Handle)value).getNode ();
}
}
}
/*
* Log
* 13 Gandalf 1.12 1/13/00 Ian Formanek NOI18N
* 12 Gandalf 1.11 1/12/00 Ian Formanek NOI18N
* 11 Gandalf 1.10 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 10 Gandalf 1.9 8/17/99 Ian Formanek Generated serial version
* UID
* 9 Gandalf 1.8 7/25/99 Ian Formanek Exceptions printed to
* console only on "netbeans.debug.exceptions" flag
* 8 Gandalf 1.7 6/9/99 Ian Formanek manifest tags changed to
* NetBeans-
* 7 Gandalf 1.6 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 6 Gandalf 1.5 6/4/99 Petr Hamernik rollback to previous
* version
* 5 Gandalf 1.4 6/4/99 Petr Hamernik temporary patch !!!!
* (extended attributes problem)
* 4 Gandalf 1.3 6/2/99 Petr Hamernik getRegisteredTypes()
* added + improvements
* 3 Gandalf 1.2 5/25/99 Petr Hamernik acceptEvent added to
* Type
* 2 Gandalf 1.1 4/23/99 Jaroslav Tulach
* 1 Gandalf 1.0 4/23/99 Jaroslav Tulach
* $
*/